package com.zhu.instructions.references;

import com.zhu.instructions.base.Index16Instruction;
import com.zhu.rtda.Frame;
import com.zhu.rtda.OperandStack;
import com.zhu.rtda.Slot;
import com.zhu.rtda.heap.Field;
import com.zhu.rtda.heap.InstanceKlass;
import com.zhu.rtda.heap.Method;
import com.zhu.rtda.heap.constant.FieldRef;
import com.zhu.rtda.heap.constant.RuntimeConstantPool;
/**
 * PUTSTATIC 指令：从操作数栈弹出一个值，并赋给指定类的静态字段。
 *
 * <p>操作码：0xB3<br>
 * 格式：putstatic indexbyte1 indexbyte2<br>
 * 操作数：index → 指向运行时常量池中的 FieldRef
 *
 * <p>执行步骤：
 * <ol>
 *   <li>解析 FieldRef 得到目标字段</li>
 *   <li>检查字段是否为 static</li>
 *   <li>若字段是 final，则仅允许在定义该字段的类的 <clinit> 方法中赋值</li>
 *   <li>【TODO】初始化字段所属类</li>
 *   <li>根据字段类型从操作数栈弹出值，写入类的 staticVars</li>
 * </ol>
 *
 * <p>关键安全检查：
 *   - 非 static 字段 → IncompatibleClassChangeError
 *   - 非法修改 final 字段 → IllegalAccessError
 */
public class PUT_STATIC extends Index16Instruction {

    @Override
    public void execute(Frame frame) {
        // 1. 获取当前方法信息（用于 final 字段检查）
        Method currentMethod = frame.getMethod();
        InstanceKlass currentClass = currentMethod.getClazz();

        // 2. 解析目标字段
        RuntimeConstantPool cp = currentClass.getConstantPool();
        FieldRef fieldRef = (FieldRef) cp.getConstant(this.index);
        Field field = fieldRef.resolvedField();

        // 3. 验证字段是否为 static
        if (!field.isStatic()) {
            throw new IncompatibleClassChangeError("Field is not static: " + field.getName());
        }

        // 4. 检查 final 字段的赋值合法性
        if (field.isFinal()) {
            InstanceKlass fieldClass = field.getClazz();
            // 只有在字段所在类的 <clinit> 方法中才允许赋值
            if (currentClass != fieldClass || !"<clinit>".equals(currentMethod.getName())) {
                throw new IllegalAccessError(
                        "Cannot modify final field outside <clinit>: " + field.getName());
            }
        }

        // 5. 【扩展点】应初始化 field 所属类（原书 todo）

        // 6. 获取静态变量存储区
        InstanceKlass fieldClass = field.getClazz();
        Slot[] staticVars = fieldClass.getStaticVars();
        int slotId = field.getSlotId();
        OperandStack stack = frame.getOperandStack();
        String descriptor = field.getDescriptor();

        // 7. 根据类型弹出值并写入 staticVars
        switch (descriptor.charAt(0)) {
            case 'Z': // boolean (stored as int: 0/1)
            case 'B': // byte
            case 'C': // char
            case 'S': // short
            case 'I': // int
                staticVars[slotId].setNum(stack.popInt());
                break;
            case 'F': // float
                float fVal = stack.popFloat();
                staticVars[slotId].setNum(Float.floatToRawIntBits(fVal));
                break;
            case 'J': // long
                long longVal = stack.popLong();
                staticVars[slotId].setNum((int) (longVal));         // low
                staticVars[slotId + 1].setNum((int) (longVal >> 32)); // high
                break;
            case 'D': // double
                double dVal = stack.popDouble();
                long doubleBits = Double.doubleToRawLongBits(dVal);
                staticVars[slotId].setNum((int) (doubleBits));         // low
                staticVars[slotId + 1].setNum((int) (doubleBits >> 32)); // high
                break;
            case 'L': // reference
            case '[': // array reference
                staticVars[slotId].setRef(stack.popRef());
                break;
            default:
                throw new RuntimeException("Unsupported field type: " + descriptor);
        }
    }
}
